package org.boz.dial;

import junit.framework.TestCase;

public class LispInterpreterTest extends TestCase {
	public void testParsePrimitives() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parse("1")[0], new LispInt(1));
		assertEquals(interpreter.parse("hello")[0], new LispSym("hello"));
		assertEquals(interpreter.parse("\"hello\"")[0], new LispString("hello"));
		assertEquals(interpreter.parse("(1 2)")[0], new LispCons(
				new LispInt(1),
				new LispCons(new LispInt(2), LispPrimitives.nil)));
	}

	public void testParseComplex() {
		LispInterpreter interpreter = new LispInterpreter();
		LispObject[] objects = interpreter.parse("1 2 3");
		assertEquals(objects.length, 3);
		assertEquals(objects[0], new LispInt(1));
		assertEquals(objects[1], new LispInt(2));
		assertEquals(objects[2], new LispInt(3));
	}

	public void testEvalSimple() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parseEval("1"), new LispInt(1));
		assertEquals(interpreter.parseEval("'hello"), new LispSym("hello"));
		assertEquals(interpreter.parseEval("\"hello\""),
				new LispString("hello"));
		assertEquals(interpreter.parseEval("(quote (1 2))"), interpreter
				.parseEval("'(1 2)"));
		assertEquals(interpreter.parseEval("1 2 3"), new LispInt(3));
	}

	public void testEvalPrimitives() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parseEval("(car '(1 2))"), new LispInt(1));
		assertEquals(interpreter.parseEval("(cdr '(1 2))"), new LispCons(
				new LispInt(2), LispPrimitives.nil));
		assertEquals(interpreter.parseEval("(cons 1 2)"), new LispCons(
				new LispInt(1), new LispInt(2)));
		assertEquals(interpreter.parseEval("'(1)"), new LispCons(
				new LispInt(1), LispPrimitives.nil));
		assertEquals(interpreter.parseEval("'(1 2)"), new LispCons(
				new LispInt(1), new LispCons(
						new LispInt(2), LispPrimitives.nil)));	
		assertEquals(
				interpreter.parseEval("(cons 1 '(2 3))"),interpreter.parseEval("'(1 2 3)"));
		assertEquals(interpreter.parseEval("(+ 1 2)"), new LispInt(3));
		assertEquals(interpreter.parseEval("(- 7 2)"), new LispInt(5));
		assertEquals(interpreter.parseEval("(* 2 8)"), new LispInt(16));
		assertEquals(interpreter.parseEval("(/ 12 4)"), new LispInt(3));
		assertEquals(interpreter.parseEval("(= 1 1)"), LispPrimitives.tee);
		assertEquals(interpreter.parseEval("(= 1 2)"), LispPrimitives.nil);
	}

	public void testStringPrimitives() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parseEval("(concat \"hello \" \"world\")"),
				new LispString("hello world"));
		assertEquals(interpreter
				.parseEval("(string-equal \"hello\" \"hello\")"),
				LispPrimitives.tee);
		assertEquals(interpreter
				.parseEval("(string-equal \"hello\" \"world\")"),
				LispPrimitives.nil);
	}

	public void testEvalForms() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parseEval("(if t 1 2)"), new LispInt(1));
		assertEquals(interpreter.parseEval("(if () 1 2)"), new LispInt(2));
		assertEquals(interpreter.parseEval("(if () 1 2 3)"), new LispInt(3));
		assertEquals(interpreter.parseEval("(if () 1)"), LispPrimitives.nil);
		assertEquals(interpreter.parseEval("((lambda () 1))"), new LispInt(1));
		assertEquals(interpreter.parseEval("((lambda (x) x) 2)"),
				new LispInt(2));
		assertEquals(interpreter.parseEval("(progn 1 2 3)"), new LispInt(3));
	}

	public void testEvalDefineSet() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parseEval("(define x 2)"), new LispInt(2));
		assertEquals(interpreter.parseEval("x"), new LispInt(2));
		assertEquals(interpreter.parseEval("(set x (+ x 1))"), new LispInt(3));
		assertEquals(interpreter.parseEval("x"), new LispInt(3));
	}

	public void testEvalDefineFunc() {
		LispInterpreter interpreter = new LispInterpreter();
		interpreter.parseEval("(define foo (lambda (x) (if x 1 2)))");
		assertEquals(interpreter.parseEval("(foo t)"), new LispInt(1));
		assertEquals(interpreter.parseEval("(foo ())"), new LispInt(2));
	}

	public void testRecursion() {
		LispInterpreter interpreter = new LispInterpreter();
		interpreter
				.parseEval("(define factorial (lambda (x) (if (= x 1) x (* x (factorial (- x 1))))))");
		assertEquals(interpreter.parseEval("(factorial 6)"), new LispInt(720));
	}

	public void testTailRecursion() {
		LispInterpreter interpreter = new LispInterpreter();
		interpreter
				.parseEval("(define factimpl (lambda (x acc) (if (= x 1) acc (factimpl (- x 1) (* x acc)))))");
		interpreter
				.parseEval("(define factorial2 (lambda (x) (factimpl x 1)))");
		// TODO: Find a way to test max stack depth
		assertEquals(interpreter.parseEval("(factorial2 6)"), new LispInt(720));
	}

	public void testEvalJava() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parseEval("(:new \"java.lang.String\")"),
				new LispString(""));
		assertEquals(interpreter
				.parseEval("(:new \"java.lang.String\" \"hello\")"),
				new LispString("hello"));
		assertEquals(
				interpreter
						.parseEval("(:invoke (:new \"java.lang.String\" \"hello\") \"length\")"),
				new LispInt(5));
		assertEquals(interpreter.parseEval("(:invoke \"hello\" \"length\")"),
				new LispInt(5));
		assertEquals(interpreter
				.parseEval("(:invoke \"hello\" \"equals\" \"hello\")"),
				LispPrimitives.tee);
		assertEquals(interpreter
				.parseEval("(:invoke \"hello\" \"equals\" \"world\")"),
				LispPrimitives.nil);
		assertEquals(
				interpreter
						.parseEval("(:invoke-static \"java.lang.String\" \"valueOf\" t)"),
				new LispString("true"));
	}

	public void testEvalLet() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parseEval("(define x 7)"), new LispInt(7));
		assertEquals(interpreter.parseEval("(let ((x 1) (y 2)) (+ x y) x)"),
				new LispInt(1));
		assertEquals(interpreter.parseEval("x"), new LispInt(7));
	}

	public void testEvalClosure() {
		LispInterpreter interpreter = new LispInterpreter();
		interpreter
				.parseEval("(define close (let ((closevar 1)) (lambda () (set closevar (+ closevar 1)))))");
		assertEquals(interpreter.parseEval("(close)"), new LispInt(2));
		assertEquals(interpreter.parseEval("(close)"), new LispInt(3));
	}
	public void testLispStartup() {
		LispInterpreter interpreter = new LispInterpreter();
		assertEquals(interpreter.parseEval("(:get-prop 1 \"MAX_VALUE\")"), new LispInt(Integer.MAX_VALUE));
		assertEquals(interpreter.parseEval("(:get-prop-static \"java.lang.Integer\" \"MAX_VALUE\")"),
				new LispInt(Integer.MAX_VALUE));
		assertEquals(interpreter.parseEval("(not ())"), LispPrimitives.tee);
		assertEquals(interpreter.parseEval("(not t)"), LispPrimitives.nil);
		assertEquals(interpreter.parseEval("(mapcar (lambda (x) (+ x 1)) '(1 2 3))"),interpreter.parseEval("'(2 3 4)"));
	}
}
